package me.chenqiang.cinc.transformers;

import java.time.LocalDate;
import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import me.chenqiang.cinc.CincCommons;
import me.chenqiang.cinc.CincCodingException;
import me.chenqiang.cinc.IDType;
import me.chenqiang.cinc.codes.ISO3166Utils;
import me.chenqiang.cinc.codes.ProvinceUtils;

public class LegacyPRIDCodec{
	private static final Pattern PATTERN = Pattern.compile("([A-Z]{3})([1-6]\\d)(\\d{2})"
			+ "(\\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|1\\d|2\\d|3[01]))(\\d)(\\d)");

	private LegacyPRIDCodec() {}
	
	public static IDType validate(String number) {
		if(!PATTERN.matcher(number).matches()) {
			return null;
		}
		if(CincCommons.calcualteICAO9303CheckCode(number.substring(0, 14)) != number.charAt(14)) {
			return null;
		}
		return IDType.LEGACY_PR_ID;
	}

	public static String compress(String number) throws CincCodingException {
		Matcher matcher = PATTERN.matcher(number);
		if(!matcher.matches()) {
			throw new CincCodingException(number);
		}
		
		final LocalDate pivot = LocalDate.of(1924, Month.JANUARY, 1);
		
		String possibleDate = String.format("19%s", matcher.group(4));
		LocalDate birthDate = LocalDate.parse(possibleDate, DateTimeFormatter.BASIC_ISO_DATE);
		if(birthDate.isBefore(pivot)) {
			possibleDate = String.format("20%s", matcher.group(4));
		}
		
		long encoded = (0x0dL << 41)
				| (long)ISO3166Utils.getCountryByAlpha3(matcher.group(1)).getIndex() << 32
				| (ProvinceUtils.getProvinceIndex(matcher.group(2)) * 100 + Long.parseLong(matcher.group(3))) << 20
				| CincCommons.calculateDaysFrom1900(possibleDate) << 4
				| Long.parseLong(matcher.group(5)); 
		
		return CincCommons.toSymbols(encoded, 9, '0');
	}

	public static String decompress(String symbols) throws CincCodingException {
		long encoded = CincCommons.toNumbers(symbols);
		
		List<String> parts = new ArrayList<>();
		parts.add(String.format("%1d", encoded & 0x0f));
		encoded >>= 4;
		parts.add(CincCommons.calculateDateByDaysFrom1900(encoded & 0xffff).substring(2));
		encoded = encoded >> 16;
		int location = (int)(encoded & 0xfff);
		parts.add(String.format("%02d", location % 100));
		parts.add(ProvinceUtils.getProvinceCode(location / 100));
		encoded >>= 12;
		parts.add(ISO3166Utils.getCountryByIndex((int)(encoded & 0x1ff)).getAlpha3());
		encoded >>= 9;
		if(encoded != 0x0d) {
			throw new IllegalArgumentException(symbols);
		}
		Collections.reverse(parts);
		
		StringBuilder sb = new StringBuilder();
		for(String p: parts) {
			sb.append(p);
		}
		
		char checkCode = CincCommons.calcualteICAO9303CheckCode(sb);
		sb.append(checkCode);
		
		return sb.toString();
	}

}
